package org.qq.tv.web.tools;

import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpRequest;
import org.apache.http.NoHttpResponseException;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.config.ConnectionConfig;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
import java.io.*;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;

public class HttpComNet {
    //全局参数
    private static PoolingHttpClientConnectionManager connectionManager = null;
    //设置请求参数
    private RequestConfig config;

    private CloseableHttpClient client;
    private Timer timer = null;

    public HttpComNet() {
        synchronized (HttpComNet.class) {
            this.init();//初始化连接池
        }
        this.startClone();
    }

    //单例模式创建
    private void init() {
        synchronized (HttpComNet.class) {
            if (client == null) {
                connectionManager = new PoolingHttpClientConnectionManager();
                // http请求线程池，最大连接数
                int requestMaxNum = 50000;
                ConnectionConfig connConfig = ConnectionConfig.custom().setCharset(StandardCharsets.UTF_8).build();
                SocketConfig socketConfig = SocketConfig.custom().setSoTimeout(5000).build();
                connectionManager.setDefaultConnectionConfig(connConfig);
                connectionManager.setDefaultSocketConfig(socketConfig);
                // 连接池最大生成连接数
                connectionManager.setMaxTotal(requestMaxNum);
                // 默认设置route最大连接数
                connectionManager.setDefaultMaxPerRoute(requestMaxNum);
                //设置请求参数
                config = RequestConfig.custom().setConnectTimeout(5000) //连接超时时间
                        .setConnectionRequestTimeout(500) //从线程池中获取线程超时时间
                        .setSocketTimeout(5000) //设置数据超时时间
                        .build();
                // 创建builder
                HttpClientBuilder builder = HttpClients.custom();
                //管理器是共享的，它的生命周期将由调用者管理，并且不会关闭
                //否则可能出现Connection pool shut down异常
                builder.setConnectionManager(connectionManager).setConnectionManagerShared(true);
                // 长连接策略
                builder.setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy());
                // 创建httpClient
                client = builder.setDefaultRequestConfig(config).setRetryHandler(new MyRetryHandle()).build();
            }
        }
    }

    /**
     * 从池子中获取连接
     *
     * @return CloseableHttpClient
     */
    private CloseableHttpClient buildHttp() {
        if (client == null) {
            init();
        }
        return client;
    }

    private String getString(String url) {
        CloseableHttpClient client = buildHttp();
        HttpGet method = new HttpGet(url);
        //设置请求头
        //method.setRequestHeader();
        long start = System.currentTimeMillis();
        BufferedReader reader = null;
        InputStream inputStream = null;
        StringBuilder result = new StringBuilder();
        try {
            long startTime = System.currentTimeMillis();
            CloseableHttpResponse response = client.execute(method);
            if (response.getStatusLine().getStatusCode() == 200) {
                HttpEntity httpEntity = response.getEntity();
                inputStream = httpEntity.getContent();
                //解析方式1
                reader = new BufferedReader(new InputStreamReader(inputStream));
                StringBuilder sb = new StringBuilder();
                String line = null;
                while ((line = reader.readLine()) != null) {
                    sb.append(line).append("\n");
                }
                String ret = sb.toString();
                System.out.println(ret);

                //解析方式二
                byte[] bytes = new byte[1024];
                int length = 0;
                while (-1 != (length = inputStream.read(bytes))) {
                    result.append(new String(bytes, 0, length));
                }
            } else {
                System.out.println("请求失败，返回" + response.getStatusLine().getStatusCode());
            }
            System.out.println("用时：" + (System.currentTimeMillis() - startTime));
            return result.toString();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (Exception e) {
                }
            }
            if (reader != null) {
                try {
                    reader.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            //释放连接
            // httpClient必须releaseConnection,但不是abort。因为releaseconnection是归还连接到到连接池,而abort是直接抛弃这个连接,而且占用连接池的数目。

            method.releaseConnection();
        }
        System.out.println("end..Duration MS:" + (System.currentTimeMillis() - start));
        return null;
    }

    public void postTest(String url, String param) {
        CloseableHttpClient client = buildHttp();
        HttpPost method = new HttpPost(url);
        //设置请求头
        //method.setRequestHeader();
        //设置请求参数--可以用各种方式传入参数
        try {
            method.setEntity(new StringEntity(param));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        long start = System.currentTimeMillis();
        BufferedReader reader = null;
        InputStream inputStream = null;
        try {
            CloseableHttpResponse response = client.execute(method);
            if (response.getStatusLine().getStatusCode() == 200) {
                HttpEntity httpEntity = response.getEntity();
                inputStream = httpEntity.getContent();
                //解析方式1
                reader = new BufferedReader(new InputStreamReader(inputStream));
                StringBuilder sb = new StringBuilder();
                String line = null;
                while ((line = reader.readLine()) != null) {
                    sb.append(line).append("\n");
                }
                String ret = sb.toString();
                System.out.println(ret);

                //解析方式二
                byte[] bytes = new byte[1024];
                int length = 0;
                StringBuilder result = new StringBuilder();
                while (-1 != (length = inputStream.read(bytes))) {
                    result.append(new String(bytes, 0, length));
                }
                System.out.println(result);
            } else {
                System.out.println("请求失败，返回" + response.getStatusLine().getStatusCode());
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (Exception e) {
                }
            }
            if (reader != null) {
                try {
                    reader.close();
                } catch (Exception e) {
                }
            }
            //关闭池子
           /* if(client != null){
                try {
                    client.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }*/
            //释放连接
            method.releaseConnection();
        }
        System.out.println("end..Duration MS:" + (System.currentTimeMillis() - start));
    }

    private void startClone() {
        // 启动定时器，定时回收过期的连接
        timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                // 关闭过期的链接
                connectionManager.closeExpiredConnections();
                // 选择关闭 空闲30秒的链接
                connectionManager.closeIdleConnections(30, TimeUnit.SECONDS);
            }
        }, 10 * 1000, 5 * 1000);
    }

    public void stopContnet() {
        timer.cancel();//停止定时回收链接
    }

    public String getHtmlToString(String path, int r) {
        try {
            HttpGet get = new HttpGet(path);
            String useragent = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36";
            get.addHeader("user-agent", useragent);
            //发送请求
            CloseableHttpResponse res = buildHttp().execute(get);
            //获取响应结果
            HttpEntity ent = res.getEntity();//请求实体

            String html = EntityUtils.toString(ent, StandardCharsets.UTF_8);
            res.close();
            get.clone();
            return html;
        } catch (Exception e) {
            synchronized (this) {

                if (r <= 0) return null;
                r--;
                return this.getHtmlToString(path, r);
            }
        }
    }

    /**
     * 请求连接池失败重试策略
     */
    private class MyRetryHandle implements HttpRequestRetryHandler {
        Logger logger = LoggerFactory.getLogger(MyRetryHandle.class);

        //请求失败时,进行请求重试
        @Override
        public boolean retryRequest(IOException e, int i, HttpContext httpContext) {
            if (i > 3) {
                //重试超过3次,放弃请求
                logger.error("retry has more than 3 time, give up request");
                return false;
            }
            if (e instanceof NoHttpResponseException) {
                //服务器没有响应,可能是服务器断开了连接,应该重试
                logger.error("receive no response from server, retry");
                return true;
            }
            if (e instanceof SSLHandshakeException) {
                // SSL握手异常
                logger.error("SSL hand shake exception");
                return false;
            }
            if (e instanceof InterruptedIOException) {
                //超时
                logger.error("InterruptedIOException");
                return false;
            }
            if (e instanceof UnknownHostException) {
                // 服务器不可达
                logger.error("server host unknown");
                return false;
            }
            if (e instanceof ConnectTimeoutException) {
                // 连接超时
                logger.error("Connection Time MyAsync");
                return false;
            }
            if (e instanceof SSLException) {
                logger.error("SSLException");
                return false;
            }

            HttpClientContext context = HttpClientContext.adapt(httpContext);
            HttpRequest request = context.getRequest();
            //如果请求不是关闭连接的请求
            return !(request instanceof HttpEntityEnclosingRequest);
        }
    }


}